为什么 `[] == []` 为 `false,[] == ![]` 为 true

相信很多面试题里都会出现“[]==![]的输出结果”这么一个题目,以前我也被问过,这个题目的结果很出乎意料居然是true,困惑了很久,一直懒得查相关资料,也就当做一个特例记下来了;最近又有一次机会碰到了这个题目,所以,这次决定要好好查查,一定要整明白,于是就有了这篇文章

怎么理解 [] == [] 为 false

这个问题很好理解,两个对象的实例化,并不相等,就像

1
2
3
var a = new Object();
var b = new Object();
console.log(a == b) // false

更深一层次的理解,可以把堆和栈两个东西搬出来。
js里面,有一个特殊的数据类型分类:基本类型、引用类型。
基本类型值包括:String,Number,Boolean,undefined,null;
引用类型值包括:Object,Array,Function,基本类型的原始类型的引用类型,以及es6新出的一些复杂数据类型;

基本类型的数值是存在栈的简单数值,而引用类型则是将实际数值存放在堆里,并同时在栈中保存一下这个实际数值的指针,这就是如何理解以下例子的原理

1
2
3
4
var a = {a:1,b:2}
var b = a;
a.a = 3;
console.log(b.a) // 3

当堆里的值发生变化的时候,所有指向这个值的指针,都会拿到变化后的值

声明引用类型的时候,程序每次都会创建出一个新的实例,存放在堆里不同的地址,引用类型数据保存的只是指向这个地址的指针,而引用类型在进行比较的时候,也就是比较两个对象的堆内存中的地址是否相同,我们上面提到的[] == []这个命题中,等式前后,都是新声明的一个空数组的实例化对象,分别会存放在两个不同的地址,因为地址不同,所以结果为false。

怎么理解 [] == ![] 为 true

首先,== 的比较规则,最后都是转换成 Number 来比较的。

== 的定义是:
The production EqualityExpression : EqualityExpression == RelationalExpression is evaluated as follows:

  • Let lref be the result of evaluating EqualityExpression.
  • Let lval be GetValue(lref).
  • Let rref be the result of evaluating RelationalExpression.
  • Let rval be GetValue(rref).
  • Return the result of performing abstract equality comparison rval == lval. (see 11.9.3).

上面提到比较的行为在 11.9.3 节里,所以翻到 11.9.3:

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  • If Type(x) is the same as Type(y), then
    - If Type(x) is Undefined, return true.
    - If Type(x) is Null, return true.
    - If Type(x) is Number, then
    - If x is NaN, return false.
    - If y is NaN, return false.
    - If x is the same Number value as y, return true.
    - If x is +0 and y is −0, return true.
    - If x is −0 and y is +0, return true.
    - Return false.
    - If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
    - If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
    - Return true if x and y refer to the same object. Otherwise, return false.
  • If x is null and y is undefined, return true.
  • If x is undefined and y is null, return true.
  • If Type(x) is Number and Type(y) is String,return the result of the comparison x == ToNumber(y).
  • If Type(x) is String and Type(y) is Number,return the result of the comparison ToNumber(x) == y.
  • If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
  • If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
  • If Type(x) is either String or Number and Type(y) is Object,return the result of the comparison x == ToPrimitive(y).
  • If Type(x) is Object and Type(y) is either String or Number,return the result of the comparison ToPrimitive(x) == y.
  • Return false.
  1. 在这段算法里,我们看到由于数组不是基本类型,需要先 ToPrimitive([])转换成原始类型, 即 “”。
    接下来 ToNumber(“”), 即 0。
  2. ![], 即 false。

结果,[] == ![] 的比较最后落到了 0 == 0 上,所以结果为 true。

参考:

https://www.zhihu.com/question/29615998/answer/45667956
http://www.uw3c.com/jsviews/js102.html

本文永久链接: https://www.mulianju.com/[]==![]/